package dianping;

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvValidationException;

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.Pattern;
import java.time.*;
import java.time.format.DateTimeFormatter;

public class DataSampler {

    private static final String INPUT_FILE = "target/dianping_clean.csv";
    private static final String OUTPUT_FILE = "target/dianping_sampled.csv";
    private static final double SAMPLE_RATIO = 1.0 / 3.0;

    // CSV列索引常量
    private static final int USER_ID_INDEX = 0;
    private static final int REST_ID_INDEX = 1;
    private static final int TIMESTAMP_INDEX = 7; // 原时间戳列索引
    private static final int COMMENT_INDEX = 8;   // 评论列索引

    // 日期格式（修改为仅包含日期部分）
    private static final DateTimeFormatter DATE_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd");

    // 其他常量
    private static final Pattern ID_PATTERN = Pattern.compile("^\\d+$");
    private static final int MAX_COMMENT_LENGTH = 500;
    private static final int MAX_SENTENCES = 3;

    public static void main(String[] args) {
        try {
            // 1. 读取、清洗并转换数据
            List<String[]> processedRecords = processRecords();

            // 2. 均匀抽样
            List<String[]> sampledRecords = sampleRecords(processedRecords);

            // 3. 写入结果文件
            writeSampledRecords(sampledRecords);

            // 4. 打印统计信息
            printStatistics(processedRecords.size(), sampledRecords.size());

        } catch (Exception e) {
            System.err.println("数据处理出错:");
            e.printStackTrace();
        }
    }

    /**
     * 读取、清洗并转换数据
     */
    private static List<String[]> processRecords() throws IOException, CsvValidationException {
        List<String[]> records = new ArrayList<>();
        int originalCount = 0;
        int filteredCount = 0;

        try (CSVReader reader = new CSVReader(new FileReader(INPUT_FILE))) {
            // 读取标题行并替换时间戳列名
            String[] header = reader.readNext();
            if (header != null) {
                // 创建新标题行，替换时间戳列名为date
                String[] newHeader = Arrays.copyOf(header, header.length);
                newHeader[TIMESTAMP_INDEX] = "date";
                records.add(newHeader);
            }

            // 处理数据行
            String[] line;
            while ((line = reader.readNext()) != null) {
                originalCount++;

                // 基本校验
                if (!isValidRecord(line)) {
                    filteredCount++;
                    continue;
                }

                // 检查评论是否为空
                if (line[COMMENT_INDEX] == null || line[COMMENT_INDEX].trim().isEmpty()) {
                    filteredCount++;
                    continue;
                }

                // 清洗评论
                line[COMMENT_INDEX] = cleanComment(line[COMMENT_INDEX]);

                // 转换时间戳为日期字符串（仅保留日期部分）
                long timestamp = Long.parseLong(line[TIMESTAMP_INDEX]);
                String date = convertTimestampToDate(timestamp);

                // 创建新行并替换时间戳
                String[] newLine = Arrays.copyOf(line, line.length);
                newLine[TIMESTAMP_INDEX] = date;

                records.add(newLine);
            }
        }

        System.out.printf("原始记录: %d, 过滤记录: %d, 保留记录: %d%n",
                originalCount, filteredCount, records.size() - 1); // -1为标题行
        return records;
    }

    /**
     * 将时间戳转换为日期字符串（仅保留日期部分）
     */
    private static String convertTimestampToDate(long timestamp) {
        return Instant.ofEpochMilli(timestamp)
                .atZone(ZoneId.systemDefault())
                .toLocalDate()
                .format(DATE_FORMATTER);
    }

    /**
     * 检查记录是否有效
     */
    private static boolean isValidRecord(String[] record) {
        // 检查字段数量
        if (record.length <= COMMENT_INDEX) {
            return false;
        }

        // 检查ID格式
        if (!isValidId(record[USER_ID_INDEX]) || !isValidId(record[REST_ID_INDEX])) {
            return false;
        }

        // 检查时间戳
        try {
            Long.parseLong(record[TIMESTAMP_INDEX]);
        } catch (NumberFormatException e) {
            return false;
        }

        return true;
    }

    /**
     * 清洗评论内容
     */
    private static String cleanComment(String comment) {
        if (comment == null) {
            return "";
        }

        // 去除多余空白字符
        comment = comment.replaceAll("\\s+", " ").trim();

        // 处理多个评论拼接的情况
        String[] sentences = comment.split("[。！？!?]");
        if (sentences.length > MAX_SENTENCES) {
            // 只保留前MAX_SENTENCES个句子
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < MAX_SENTENCES && i < sentences.length; i++) {
                if (!sentences[i].trim().isEmpty()) {
                    sb.append(sentences[i].trim());
                    sb.append(i < MAX_SENTENCES - 1 ? "。" : "");
                }
            }
            comment = sb.toString();
        }

        // 限制评论长度
        if (comment.length() > MAX_COMMENT_LENGTH) {
            comment = comment.substring(0, MAX_COMMENT_LENGTH) + "...";
        }

        return comment;
    }

    /**
     * 验证ID格式
     */
    private static boolean isValidId(String id) {
        return id != null && ID_PATTERN.matcher(id).matches();
    }

    /**
     * 均匀抽样
     */
    private static List<String[]> sampleRecords(List<String[]> records) {
        // 排除标题行
        int dataSize = records.size() - 1;
        if (dataSize <= 0) {
            return records;
        }

        // 计算抽样数量
        int sampleSize = Math.max(1, (int) Math.ceil(dataSize * SAMPLE_RATIO));
        double step = (double) dataSize / sampleSize;

        // 抽样
        List<String[]> samples = new ArrayList<>();
        samples.add(records.get(0)); // 添加标题行

        for (int i = 0; i < sampleSize; i++) {
            int index = 1 + (int) Math.round(i * step); // +1跳过标题行
            index = Math.min(index, records.size() - 1);
            samples.add(records.get(index));
        }

        return samples;
    }

    /**
     * 写入采样结果
     */
    private static void writeSampledRecords(List<String[]> sampledRecords) throws IOException {
        Files.createDirectories(Paths.get(OUTPUT_FILE).getParent());
        try (CSVWriter writer = new CSVWriter(new FileWriter(OUTPUT_FILE))) {
            writer.writeAll(sampledRecords);
        }
    }

    /**
     * 输出统计信息
     */
    private static void printStatistics(int cleanedCount, int sampledCount) {
        System.out.println("数据采样完成");
        System.out.println("输出文件: " + Paths.get(OUTPUT_FILE).toAbsolutePath());
        System.out.printf("清洗后记录数: %d, 采样记录数: %d (比例: %.2f)%n",
                cleanedCount - 1, // 减去标题行
                sampledCount - 1, // 减去标题行
                SAMPLE_RATIO);
    }
}